package com.yvan.leto.server.service;

import com.baomidou.mybatisplus.plugins.pagination.Pagination;
import com.baomidou.mybatisplus.toolkit.IdWorker;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.yvan.Conv;
import com.yvan.leto.ConfigRegistry;
import com.yvan.leto.server.config.LetoServerProperties;
import com.yvan.leto.server.dao.ProjectHeadMapper;
import com.yvan.leto.server.dao.ProjectPersistent;
import com.yvan.leto.server.dao.ReleaseLogMapper;
import com.yvan.leto.server.dao.ReleaseProjectMapper;
import com.yvan.leto.server.domain.*;
import com.yvan.leto.server.security.JwtHelper;
import com.yvan.leto.server.utils.FileUtils;
import com.yvan.leto.server.utils.GitUtils;
import com.yvan.platform.StringUtils;
import lombok.NonNull;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.eclipse.jgit.api.Git;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.File;
import java.util.Collection;
import java.util.List;
import java.util.Map;

@Slf4j
@Service
@Transactional(readOnly = true)
public class ReleaseService {

    private static final String CACHE_PATH = "cache/";

    private final LetoServerProperties properties;
    private final Git git;
    private final ProjectPersistent projectPersistent;
    private final ReleaseLogMapper releaseLogMapper;
    private final ReleaseProjectMapper releaseProjectMapper;
    private final ProjectHeadMapper projectHeadMapper;
    private final ConfigRegistry configRegistry;
    private final ConfigParserService configParserService;

    @Autowired
    @SneakyThrows
    public ReleaseService(LetoServerProperties properties,
                          ProjectPersistent projectPersistent,
                          ReleaseLogMapper releaseLogMapper,
                          ReleaseProjectMapper releaseProjectMapper,
                          ProjectHeadMapper projectHeadMapper,
                          ConfigRegistry configRegistry,
                          ConfigParserService configParserService) {

        this.properties = properties;
        this.projectPersistent = projectPersistent;
        this.releaseLogMapper = releaseLogMapper;
        this.releaseProjectMapper = releaseProjectMapper;
        this.projectHeadMapper = projectHeadMapper;
        this.configRegistry = configRegistry;
        this.configParserService = configParserService;

        val wf = this.properties.getGit().getWorkfolder();
        if (StringUtils.isNotEmpty(wf)) {
            git = Git.open(new File(wf));
        } else {
            git = null;
        }
    }

    @Transactional
    public List<ReleaseLog> logs(Pagination pagination) {
        return releaseLogMapper.selectByPage(pagination);
    }

    @Transactional
    public void publishProject(@NonNull String env, @NonNull String project, @NonNull String msg) {
        publishInner(msg, Lists.newArrayList(new ReleaseUnit(env, project)));
    }

    @Transactional
    public void publishEnv(String env, String msg) {

        String[] projectNames = projectPersistent.projectFindAll();
        List<ReleaseUnit> units = Lists.newArrayList();
        for (String project : projectNames) {
            units.add(new ReleaseUnit(env, project));
        }
        publishInner(msg, units);
    }

    private void publishInner(String msg, Collection<ReleaseUnit> releaseUnits) {

        final long revId = IdWorker.getId();

        //生成内容
        for (ReleaseUnit unit : releaseUnits) {
            Project project = projectPersistent.projectFindByName(unit.getProject());

            for (Segment segment : project.getSegments()) {
                final IParser parser = configParserService.getParser(segment);
                final String content = parser.process(new ParserContext(unit.getEnv(), project, segment));
                setConfigResult(unit.getEnv(), unit.getProject(), segment.getName(), Long.toString(revId), content);
            }
        }

        //生成发布日志
        ReleaseLog releaseLog = new ReleaseLog();
        String sha;
        if (git != null) {
            //提交git
            sha = GitUtils.commitWorkfolder(git, msg);
        } else {
            sha = "";
        }
        releaseLog.setRevId(revId);
        releaseLog.setSha(sha);
        releaseLog.setMsg(msg);
        releaseLog.setOper(JwtHelper.getLoginName());
        releaseLogMapper.insert(releaseLog);

        for (ReleaseUnit unit : releaseUnits) {
            //记录项目目录
            ProjectHead head = new ProjectHead();
            head.setEnv(unit.getEnv());
            head.setProject(unit.getProject());
            head.setRevId(revId);
            projectHeadMapper.insertOrUpdate(head);

            //记录项目发布记录
            ReleaseProject releaseProject = new ReleaseProject();
            releaseProject.setEnv(unit.getEnv());
            releaseProject.setId(IdWorker.getId());
            releaseProject.setProject(unit.getProject());
            releaseProject.setRevId(revId);
            releaseProject.setSha(sha);
            releaseProjectMapper.insert(releaseProject);

            //推送变更
            this.configRegistry.updateProjectVersion(unit.getEnv(), unit.getProject(), Long.toString(revId));
        }
    }

    public boolean existsResult(String env, String projectName, String segmentName) {
        String revId = projectHeadMapper.selectReleaseUnit(env, projectName);
        if (StringUtils.isBlank(revId)) {
            return false;
        }
        return FileUtils.exists(properties.getRepository(), CACHE_PATH, revId, env + "." + projectName + "." + segmentName);
    }

    /**
     * 获取某个版本配置段的结果
     */
    public String getConfigResult(String env, String projectName, String segmentName) {
        String revId = projectHeadMapper.selectReleaseUnit(env, projectName);
        return FileUtils.readContent(properties.getRepository(), CACHE_PATH, revId, env + "." + projectName + "." + segmentName);
    }

    /**
     * 设置某个版本配置段的结果
     */
    private void setConfigResult(String env, String projectName, String segmentName, String revId, String content) {
        File file = FileUtils.getWriteFile(properties.getRepository(), CACHE_PATH, revId, env + "." + projectName + "." + segmentName);
        FileUtils.write(content, file);
    }

    public Map<String, Map<String, Object>> selectProjectSummary(String env, String[] projects) {
        val list = projectHeadMapper.selectSummary(env, projects);
        Map<String, Map<String, Object>> r = Maps.newHashMap();
        for (Map<String, Object> row : list) {
            r.put(Conv.NS(row.get("project")), row);
        }
        return r;
    }

    /*
    public void publishEnv(String env) {
        for (String projectName : projectPersistent.projectFindAll()) {
            publishEnv(env, projectName);
        }
    }

    public void publishEnv(String env, String projectName) {
        Project project = projectPersistent.projectFindByName(projectName);
        String projectVersion = Conv.NS(System.currentTimeMillis());
        configRegistry.updateProjectVersion(env, projectName, projectVersion);

        for (Segment segment : project.getSegments()) {
            publishEnv(env, projectName, SegmentHelper.createSegmentName(segment.getName(), segment.getType()));
        }
    }

    public void publishEnv(String env, String projectName, String segment) {
        String segmentVersion = Conv.NS(System.currentTimeMillis());
        configRegistry.updateSegmentVersion(env, projectName, segment, segmentVersion);
    }
    */
}
